package com.loie.xcx.syUtil;

import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Enumeration;
import java.util.regex.Pattern;

/**
 * Created by shanfq on 2018/8/31.
 */
public class EncryptUtils {

    public static byte[] getSha256Data(String oriText) throws NoSuchAlgorithmException {
        return getSha256Data(oriText, 1);
    }

    public static byte[] getSha256Data(String oriText, int iterations) throws NoSuchAlgorithmException {
        try {
            MessageDigest e = MessageDigest.getInstance("SHA-256");

            byte[] result = e.digest(oriText.getBytes());

            for (int i = 1; i < iterations; ++i) {
                e.reset();
                result = e.digest(result);
            }

            return result;
        } catch (Exception e) {
//            log.error("获取摘要信息失败:{}", oriText);
//            log.error("错误详情", e);
            throw e;
        }

    }


    /**
     * 签名
     *
     * @param plainText  签名原文
     * @param algorithm  签名算法
     * @param privateKey 私钥
     * @return 签名结果(BASE64)
     * @throws Exception 签名异常
     */
    public static String sign(String plainText, String algorithm, PrivateKey privateKey) throws Exception {
        try {
            Signature sig = Signature.getInstance(algorithm);
            sig.initSign(privateKey);
            sig.update(plainText.getBytes());
            byte[] b = sig.sign();
            return Base64.getEncoder().encodeToString(b);
        } catch (NoSuchAlgorithmException e) {
//            log.error("签名异常，不支持算法[{}]", algorithm);
            throw new Exception(e);
        } catch (SignatureException e) {
//            log.error("签名异常，异常原因[{}]", e.getMessage());
            throw new Exception(e);
        } catch (InvalidKeyException e) {
//            log.error("签名异常，无效密钥[{}]", privateKey);
            throw new Exception(e);
        }
    }

    /**
     * 从classpath下读取文件内容
     *
     * @param path 资源路径
     * @return 文件内容（base64编码)
     * @throws IOException
     */
    public static String readBase64FromResource(String path) throws IOException, URISyntaxException {
        Resource resource = new ClassPathResource(path);
//        Resource resource = new FileSystemResource(path);
        return readBase64FromStream(resource.getInputStream(), (int) resource.contentLength());
    }

    /**
     * 根据指定的文件路径读取文件内容
     *
     * @param path 文件绝对路径或相对路径
     * @return 文件内容（base64编码)
     * @throws IOException
     * @throws URISyntaxException
     */
    public static String readBase64FromFile(String path) throws IOException {
        File file = new File(path);
        return readBase64FromFile(file);
    }

    public static String readBase64FromStream(InputStream is, int length) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream(length);
        byte[] buffer = new byte[length];
        int readLength = 0;
        while ((readLength = is.read(buffer)) >= 0) {
            baos.write(buffer, 0, readLength);
        }
        try {
            is.close();
        } catch (IOException ex) {
//            log.error("关闭文件流发生异常!{}");
        }
        String fileContent = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(baos.toByteArray());
        return fileContent;
    }

    /**
     * 读取指定文件的内容
     *
     * @param file 文件
     * @return 文件内容(Base64编码)
     * @throws IOException
     */
    public static String readBase64FromFile(File file) throws IOException {
        int iBuffSize = (int) file.length();
        InputStream is = new FileInputStream(file);
        ByteArrayOutputStream baos = new ByteArrayOutputStream(iBuffSize);
        byte[] buffer = new byte[iBuffSize];
        int readLength = 0;
        while ((readLength = is.read(buffer)) >= 0) {
            baos.write(buffer, 0, readLength);
        }
        try {
            is.close();
        } catch (IOException ex) {
//            log.error("关闭文件流发生异常!{}" + file.getPath());
        }
        String fileContent = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(baos.toByteArray());
        return fileContent;
    }

    /**
     * 获取私钥
     *
     * @param pfxContent 私钥证书Base64密文
     * @param password   私钥密码
     * @return 私钥
     * @throws Exception 解析异常
     */
    public static PrivateKey readPrivateKey(String pfxContent, String password, String keyStoreType) throws Exception {
        char[] pass = password.toCharArray();
        try {
            KeyStore ks = KeyStore.getInstance(keyStoreType);
            ByteArrayOutputStream os = new ByteArrayOutputStream(pfxContent.length());
            ks.load(new ByteArrayInputStream(Base64.getDecoder().decode(pfxContent)), pass);
            Enumeration<String> aliases = ks.aliases();
            String alias = aliases.nextElement();
            if (null == alias) {
//                log.error("解析证书失败，找不到证书别名");
                throw new Exception("解析证书失败，找不到证书别名");
            }
            PrivateKey privateKey = (PrivateKey) ks.getKey(alias, pass);
            return privateKey;
        } catch (KeyStoreException e) {
//            log.error("读取私钥异常KeyStoreType[{}]" + keyStoreType);
            throw new Exception(e);
        } catch (CertificateException e) {
            throw new Exception(e);
        } catch (UnrecoverableKeyException e) {
            throw new Exception(e);
        } catch (NoSuchAlgorithmException e) {
//            log.error("读取私钥失败，找不到支持的算法");
            throw new Exception(e);
        } catch (IOException e) {
            throw new Exception(e);
        }
    }

    /**
     * 读取证书中的私钥
     *
     * @param pfxPath
     * @param password
     * @param keyStoreType
     * @return
     * @throws Exception
     */
    public static PrivateKey getPrivateKey(String pfxPath, String password, String keyStoreType) throws Exception {
//        String fileContent = EncryptUtils.readBase64FromFile(pfxPath);
        String fileContent = EncryptUtils.readBase64FromResource(pfxPath);
        System.out.println(fileContent);
        PrivateKey privateKey = EncryptUtils.readPrivateKey(fileContent, password, keyStoreType);
        return privateKey;
    }


    /**
     * 读取证书中的公钥
     *
     * @param cerPath
     * @return
     * @throws Exception
     */
    public static PublicKey getPublicKey(String cerPath) throws Exception {
        if (cerPath.endsWith("pem")) {
            return getPemPublicKey(cerPath);
        }

        String cerContent = EncryptUtils.readBase64FromResource(cerPath);
        X509Certificate certificate = StringUtil.loadCertificate(cerContent);
        PublicKey publicKey = certificate.getPublicKey();
        return publicKey;
    }


    /**
     * 读取pem格式的公钥
     *
     * @param pemPath
     * @return
     * @throws Exception
     */
    public static PublicKey getPemPublicKey(String pemPath) throws Exception {
//        InputStream in = new FileInputStream(pemPath);
        InputStream in = (new ClassPathResource(pemPath)).getInputStream();
//        InputStream in = (new FileSystemResource(pemPath)).getInputStream();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buf = new byte[1024];
        for (int read = 0; read != -1; read = in.read(buf)) {
            baos.write(buf, 0, read);
        }
        String pem = new String(baos.toByteArray(), StandardCharsets.ISO_8859_1);
        Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
        String encoded = parse.matcher(pem).replaceFirst("$1");
        byte[] bytes = Base64.getMimeDecoder().decode(encoded);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(bytes));
        return publicKey;
    }
}
